/// <reference path="../Resources/js/jquery/jquery.debug.js" />
/// <reference name="MicrosoftAjax.debug.js" />

function MBasis() {
    ///	<summary locid="T:J#MBasis">
    ///	This is a class that holds data about base definitions of MForm items.
    /// All MForm items must be registered within their corresponding basis.
    /// The basis also takes care of the parent-child relationship between MForm items.
    ///	</summary>
    MBasis.initializeBase(this, []);
    this.minOccurs = 1;
    this.maxOccurs = 1;
    this.children = new Array();
    this.instances = new Object();
    this.lastParts = new Array();
    this.mode = MItem.EditMode;
}

MBasis.prototype = {
    add_Changed: function (handler) {
        /// <summary>
        /// Add an event handler for the Changed event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Changed', handler);
    },

    add_DescriptionChanged: function (handler) {
        /// <summary>
        /// Add an event handler for the DescriptionChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('DescriptionChanged', handler);
    },

    add_Focused: function (handler) {
        /// <summary>
        /// Add an event handler for the Focused event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Focused', handler);
    },

    add_Loaded: function (handler) {
        /// <summary>
        /// Add an event handler for the Loaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Loaded', handler);
    },

    add_Loading: function (handler) {
        /// <summary>
        /// Add an event handler for the Loading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Loading', handler);
    },

    add_ValidationError: function (handler) {
        /// <summary>
        /// Add an event handler for the ValidationError event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('ValidationError', handler);
    },

    add_ValidationSuccess: function (handler) {
        /// <summary>
        /// Add an event handler for the ValidationSuccess event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('ValidationSuccess', handler);
    },

    add_Unloading: function (handler) {
        /// <summary>
        /// Add an event handler for the Unloading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Unloading', handler);
    },

    add_Unloaded: function (handler) {
        /// <summary>
        /// Add an event handler for the Unloaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Unloaded', handler);
    },

    add_UsageChanged: function (handler) {
        /// <summary>
        /// Add an event handler for the UsageChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('UsageChanged', handler);
    },

    get_parentId: function () {
        /// <returns type="string" >
        /// returns the parent basis id       
        /// </returns>
        return this.parentId;
    },

    set_parentId: function (parentId) {
        /// <summary>
        /// sets the parent basis id  
        /// </summary>
        this.parentId = parentId;
    },

    get_mode: function () {
        /// <returns type="string" >
        /// returns the mode of the basis
        /// </returns>
        return this.mode;
    },

    set_mode: function (mode) {
        /// <summary>
        /// sets the mode of the basis  
        /// </summary>
        this.mode = mode;
    },

    get_qualifiedName: function () {
        /// <returns type="string" >
        /// returns the basis qualified name string representation       
        /// </returns>
        return this.qualifiedName;
    },

    set_qualifiedName: function (qualifiedName) {
        /// <summary>
        /// sets the basis qualified name string representation 
        /// </summary>
        this.qualifiedName = qualifiedName;
    },

    set_rootId: function (rootId) {
        /// <summary>
        /// sets the MForm root id
        /// </summary>
        this.rootId = rootId;
    },

    get_rootId: function () {
        /// <returns type="string" >
        /// returns the MForm root id       
        /// </returns>
        return this.rootId;
    },

    set_dataType: function (dataType) {
        /// <summary>
        /// sets the dataType
        /// </summary>
        this.dataType = dataType;
    },

    get_dataType: function () {
        /// <returns type="int" >
        /// returns the dataType       
        /// </returns>
        return this.dataType;
    },

    getRootWithParentId: function () {
        var builder = new Sys.StringBuilder(this.get_rootId());
        if (this.get_parentId() != null) {
            builder.append('_0_');
            builder.append(this.get_parentId());
        }
        builder.append('_0');
        return builder.toString();
    },

    get_lastParts: function () {
        /// <returns type="Array" >
        /// returns the last part control ids  
        /// </returns>        
        return this.lastParts;
    },

    set_lastParts: function (lastParts) {
        /// <summary>
        /// sets the lastParts
        /// </summary>
        this.lastParts = lastParts;
    },

    get_lastPart: function (idSeparator) {
        /// <returns type="string" >
        /// returns the lastPart  
        /// </returns>
        idSeparator = idSeparator || '_';
        return this.get_lastParts().join(idSeparator);
    },

    get_id: function () {
        /// <returns type="string" >
        /// returns the basis prototype id - the prototype id is filled with 0's in place of MForm item position locators
        /// </returns>
        if (this.id == null) {
            var builder = new Sys.StringBuilder(this.getRootWithParentId());
            var lastPart = this.get_lastPart();
            if (lastPart != '') {
                builder.append('_');
                builder.append(lastPart);
                builder.append('_0');
            }
            var idPath = new Path(builder.toString());
            this.id = idPath.getPrototypePath();
        }

        return this.id;
    },

    set_minOccurs: function (minOccurs) {
        /// <summary>
        /// sets the basis minimal occurs
        /// </summary>
        this.minOccurs = minOccurs;
    },

    get_minOccurs: function () {
        /// <returns type="number" >
        /// returns the basis minimal occurs
        /// </returns>
        return this.minOccurs;
    },

    set_minRenderedOccurs: function (minRenderedOccurs) {
        /// <summary>
        /// sets the number of occurs when the items are added        
        /// </summary>
        this.minRenderedOccurs = minRenderedOccurs;
    },

    get_minRenderedOccurs: function () {
        /// <returns type="number" >
        /// returns the number of occurs when the items are added       
        /// by default this value equals minOccurs
        /// </returns>
        return this.minRenderedOccurs || this.get_minOccurs();
    },

    set_maxOccurs: function (maxOccurs) {
        /// <summary>
        /// sets the basis maximal occurs
        /// </summary>
        this.maxOccurs = maxOccurs;
    },

    get_maxOccurs: function () {
        /// <returns type="number" >
        /// returns the basis maximal occurs
        /// </returns>
        return this.maxOccurs;
    },

    getMForm: function () {
        /// <summary>
        /// returns the root MForm object
        /// </summary>    
        /// <returns type="MForm" >
        /// the the root MForm object
        /// </returns>
        return $find(this.get_rootId());
    },

    getChild: function (qualifiedName) {
        /// <summary>
        /// returns the child object with the specified qualified name
        /// </summary>    
        /// <returns type="MBasis" >
        /// the child object with the specified qualified name
        /// </returns>
        for (var i = 0; i < this.getChildren().length; i++) {
            var child = this.getChildren()[i];
            if (child.get_qualifiedName() == qualifiedName) {
                return child;
            }
        }

        return null;
    },

    getChildren: function () {
        return this.children;
    },

    initialize: function () {
        MBasis.callBaseMethod(this, 'initialize');
        this.determineParent();
    },

    setPrototypeNode: function (node) {
        /// <summary>
        /// sets the prototype node that will be cloned when new elements are added
        /// </summary>
        if (this.node != null && !node.id.endsWith('0')) {
            return;
        }
        this.node = node;
    },

    dispose: function () {
        MBasis.callBaseMethod(this, 'dispose');
        for (var key in this.instances) {
            delete this.instances[key];
        }
        this.node = null;
        this.instances = null;
        this.parent = null;
    },

    determineParent: function () {
        if (this.get_lastPart() == '') {
            this.isRoot = true;
            return;
        }

        this.parent = MBasis.getById(this.getRootWithParentId());
        this.parent.children.push(this);
    },

    getName: function () {
        return this.node.getAttribute("name");
    },

    getDescendantInstances: function (id) {
        id = id + '_';
        var instances = new Array();
        for (var key in this.instances) {
            if (key.startsWith(id)) {
                instances.push(this.instances[key]);
            }
        }
        return instances;
    },

    getAncestorInstance: function (id) {
        for (var key in this.instances) {
            if (id.startsWith(key + '_')) {
                return this.instances[key];
            }
        }
        return null;
    },

    getInstance: function (id) {
        var path = new Path(id);
        return this.instances[path.getNodePath()];
    },

    createInstance: function (node) {
        this.instances[node.id] = MItem.create(node, this);
        this.getMForm().raise_itemCreated({ 'item': this.instances[node.id] });
        return this.instances[node.id];
    },

    removeInstance: function (id) {
        var instance = this.instances[id];
        this.getMForm().raise_itemRemoved({ 'item': instance });
        delete this.instances[id];
        instance.raiseUnloaded();
    },

    incrementAvailableIndex: function (parentItem) {
        var identifier = parentItem.getId() + "_" + this.get_lastPart();
        var hiddenInput = this._createOrGetHiddenInput(identifier);
        hiddenInput.value = (parseInt(hiddenInput.value) + 1).toString();
    },

    getAvailableIndex: function (parentItem) {
        var identifier = parentItem.getId() + "_" + this.get_lastPart();
        var hiddenInput = this._createOrGetHiddenInput(identifier);                
        return (parseInt(hiddenInput.value) + 1).toString();
    },

    register: function (node) {
        var elems = $(node).find('.' + MItem.Element).andSelf();
        for (var i = 0; i < elems.length; i++) {
            MBasis.getById(elems[i].id).registerSingle(elems[i]);
        }
    },

    registerSingle: function (node) {
        this._createHiddenInput(node);
        this.createInstance(node);
    },

    _createHiddenInput: function (node) {
        if (this.get_minOccurs() == this.get_maxOccurs()) {
            return;
        }

        var identifier = node.id.substring(0, node.id.lastIndexOf('_'));
        this._createOrGetHiddenInput(identifier);
    },

    _createOrGetHiddenInput: function (identifier) {
        var hiddenInput = this._getHiddenInput(identifier);
        if (hiddenInput == null) {
            $("#" + this.get_rootId()).append("<input type='hidden' name='" + identifier + "' value='" + this.get_minRenderedOccurs() + "' />");
        }

        return this._getHiddenInput(identifier);
    },

    _getHiddenInput: function (identifier) {
        return $("#" + this.get_rootId() + ">input[name = '" + identifier + "']")[0];
    },

    unregister: function (item) {
        var children = item.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].basis.unregister(children[i]);
        }
        this.removeInstance(item.getId());
    },

    getNodeIndex: function () {
        var id = this.node.id;
        var underscoreIndex = id.lastIndexOf("_");
        return id.substring(underscoreIndex + 1);
    },

    getAllLastParts: function () {
        var allLastParts = (this.parent != null) ? this.parent.getAllLastParts() : new Array();
        var lastParts = this.get_lastParts();
        for (var i = 0; i < lastParts.length; i++) {
            allLastParts.push(lastParts[i]);
        }
        return allLastParts;
    },

    getUniqueId: function (id, idSeparator) {
        var nodeId = id.substring(this.get_rootId().length);
        var allLastParts = this.getAllLastParts();
        for (var i = 0; i < allLastParts.length; i++) {
            nodeId = nodeId.split(allLastParts[i]).join('{' + i + '}');
        }
        var nodeId = nodeId.split('_').join(idSeparator);
        for (var i = 0; i < allLastParts.length; i++) {
            nodeId = nodeId.split('{' + i + '}').join(allLastParts[i]);
        }

        return nodeId;
    }
}

MBasis.registerClass("MBasis", Sys.Component);

MBasis.getById = function(id) {
    var idPath = new Path(id);
    var prototypeId = idPath.getPrototypePath();
    return $find(prototypeId);
}

MBasis.add = function(text) {
    var splitText = text.split('/');
    var parentId = splitText[0];
    var qualifiedName = splitText[1];
    MItem.getById(parentId).add(qualifiedName);
}